package com.nlx.notes.module.service.impl.loan;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import com.nlx.notes.configuer.redis.annotation.RedisLock;
import com.nlx.notes.module.bean.bo.LoanDetailBO;
import com.nlx.notes.module.bean.bo.LoanOverviewBO;
import com.nlx.notes.module.bean.bo.LoanSummaryBO;
import com.nlx.notes.module.bean.dto.FlowSaveDTO;
import com.nlx.notes.module.bean.dto.flow.FlowEditDTO;
import com.nlx.notes.module.bean.dto.loan.*;
import com.nlx.notes.module.bean.vo.PageResultVo;
import com.nlx.notes.module.bean.vo.Pagination;
import com.nlx.notes.module.bean.vo.loan.*;
import com.nlx.notes.module.core.consts.GlobalConst;
import com.nlx.notes.module.core.enums.LoanTypeEnum;
import com.nlx.notes.module.core.enums.TransactionTypeEnum;
import com.nlx.notes.module.core.enums.error.CategoryCodeEnum;
import com.nlx.notes.module.core.enums.error.LoanCodeEnum;
import com.nlx.notes.module.core.util.AmountConverterUtils;
import com.nlx.notes.module.core.util.DataConvertorUtils;
import com.nlx.notes.module.entity.BorrowerInfo;
import com.nlx.notes.module.entity.Categories;
import com.nlx.notes.module.entity.LoanDetail;
import com.nlx.notes.module.entity.LoanSummary;
import com.nlx.notes.module.service.ICategoriesService;
import com.nlx.notes.module.service.ITransactionsService;
import com.nlx.notes.module.service.flow.FlowManageService;
import com.nlx.notes.module.service.loan.IBorrowerInfoService;
import com.nlx.notes.module.service.loan.ILoanDetailService;
import com.nlx.notes.module.service.loan.ILoanSummaryService;
import com.nlx.notes.module.service.loan.LoanManageService;
import com.nlx.notes.support.handler.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class LoanManageServiceImpl implements LoanManageService {

    final ILoanSummaryService loanSummaryService;

    final FlowManageService flowManageService;

    final ITransactionsService transactionsService;

    final ICategoriesService categoriesService;

    final ILoanDetailService loanDetailService;

    final IBorrowerInfoService borrowerInfoService;


    @Override
    public PageResultVo<LoanSummaryVO> pageByCondition(LoanSummaryQueryDTO loanSummaryQueryDTO) {
        long count = loanSummaryService.countByCondition(loanSummaryQueryDTO);
        Pagination pagination = new Pagination(loanSummaryQueryDTO.getPage(), count, loanSummaryQueryDTO.getPageSize());
        if(0 == count){
            PageResultVo.success(pagination, Collections.emptyList());
        }
        List<LoanSummaryBO> loanSummaryList = loanSummaryService.pageByCondition(loanSummaryQueryDTO);
        List<LoanSummaryVO> list = new ArrayList<>(loanSummaryList.size());
        if(CollUtil.isNotEmpty(loanSummaryList)){
            list = DataConvertorUtils.copyListProperties(loanSummaryList,LoanSummaryVO::new,(source, target) ->{
                target.setTotalLoan(AmountConverterUtils.convertFenToYuan(source.getTotalLoan()));
                target.setTotalRepay(AmountConverterUtils.convertFenToYuan(source.getTotalRepay()));
                target.setRemainAmount(AmountConverterUtils.convertFenToYuan(source.getRemainAmount()));
            });
        }
        log.info("数据库查询到的数据：{}", list);
        return PageResultVo.success(pagination, list);
    }

    @Override

    @Transactional(rollbackFor = Exception.class)
    public LoanSummaryVO saveLoan(LoanSummarySaveDTO loanSummarySaveDTO) {
        loanSummarySaveDTO.setAmountInt(AmountConverterUtils.convertYuanToFen(loanSummarySaveDTO.getAmount()));
        //判断借款人是否存在借款
        List<String> statusList = ListUtil.of(GlobalConst.Loan.LOAN_STATUS_AWAIT_REPAY, GlobalConst.Loan.LOAN_STATUS_ONGOING);
        LoanSummary loanSummary = loanSummaryService.findByBorrowerIdAndStatus(loanSummarySaveDTO.getBorrowerId(),statusList,
                loanSummarySaveDTO.getUserId());
        BorrowerInfo borrowerInfo = borrowerInfoService.findAndCheckById(loanSummarySaveDTO.getBorrowerId(), loanSummarySaveDTO.getUserId());
        if(null == borrowerInfo){
            log.error("选择的借款人不存在");
            throw new BusinessException(LoanCodeEnum.LOAN_BORROWER_NAME_NOT_EXISTS_ERROR);
        }
        LoanSummaryVO loanSummaryVO = null;
        Long flowId = null;
        if(loanSummarySaveDTO.getSaveFlow()){
            Categories categories = categoriesService.findByCateGoryCode("renqing_jiech",loanSummarySaveDTO.getUserId());
            if(categories == null ){
                log.error("当前用户没有借出的分类，可能被删除了！");
                throw new BusinessException(CategoryCodeEnum.CATEGORY_NOT_EXIST);
            }
            FlowSaveDTO flowSaveDTO  = new FlowSaveDTO();
            flowSaveDTO.setTransactionType(TransactionTypeEnum.EXPEND.getCode());
            flowSaveDTO.setAmount(loanSummarySaveDTO.getAmount());
            flowSaveDTO.setCategoryId(categories.getId());
            flowSaveDTO.setAccountId(loanSummarySaveDTO.getAccountId());
            flowSaveDTO.setTransactionDate(loanSummarySaveDTO.getLoanDate());
            flowSaveDTO.setUserId(loanSummarySaveDTO.getUserId());
            flowSaveDTO.setDescription("借给"+borrowerInfo.getName());
            flowId = transactionsService.saveFlow(flowSaveDTO);
        }
        loanSummarySaveDTO.setFlowId(flowId);
        if(null == loanSummary){
            loanSummary = loanSummaryService.saveLoan(loanSummarySaveDTO);
            if(null == loanSummary){
                log.error("保存借款信息失败");
                throw new BusinessException(LoanCodeEnum.SAVE_ERROR_UNKNOWN_ERROR);
            }
        }else{
            //借款累加
            boolean flag = loanSummaryService.incTotalLoan(loanSummarySaveDTO.getAmountInt(),
                    loanSummarySaveDTO.getLoanDate(),loanSummary.getId());
            if(!flag){
                log.error("累加借款数据失败");
                throw new BusinessException(LoanCodeEnum.SAVE_ERROR_UNKNOWN_ERROR);
            }
        }
        loanSummarySaveDTO.setSummaryId(loanSummary.getId());
        Boolean flag = loanDetailService.saveLoan(loanSummarySaveDTO);
        if(!flag){
            log.error("保存借款信息失败");
            throw new BusinessException(LoanCodeEnum.SAVE_ERROR_UNKNOWN_ERROR);
        }
        return loanSummaryVO;
    }

    @Override
    public PageResultVo<LoanDetailPageVO> loanDetailPage(LoanDetailQueryDTO loanDetailQueryDTO) {
        long count = loanDetailService.countByCondition(loanDetailQueryDTO);
        Pagination pagination = new Pagination(loanDetailQueryDTO.getPage(), count, loanDetailQueryDTO.getPageSize());
        if(0 == count){
            return PageResultVo.success(pagination, Collections.emptyList());
        }
        List<LoanDetailBO> loanDetailList = loanDetailService.selectPageByCondition(loanDetailQueryDTO);
        if(CollUtil.isEmpty(loanDetailList)){
            return PageResultVo.success(pagination, Collections.emptyList());
        }
        List<LoanDetailPageVO> list = new ArrayList<>(loanDetailList.size());
        if(CollUtil.isNotEmpty(loanDetailList)){
            list = DataConvertorUtils.copyListProperties(loanDetailList,LoanDetailPageVO::new,(source, target) ->{
                target.setAmount(AmountConverterUtils.convertFenToYuan(source.getAmount()));
            });
        }
        return PageResultVo.success(pagination, list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoanSummaryVO updateLoan(LoanSummaryUpdateDTO loanSummaryUpdateDTO) {
        LoanDetail loanDetail = this.loanDetailService.findById(loanSummaryUpdateDTO.getId(),loanSummaryUpdateDTO.getUserId());
        if(null == loanDetail){
            log.error("当前借款信息不存在");
            throw new BusinessException(LoanCodeEnum.DATA_NOT_EXISTS_ERROR);
        }
        boolean flag = this.loanDetailService.updateLoanDetail(loanSummaryUpdateDTO);
        if(!flag){
            log.error("更新借款明细失败，原因未知");
            throw new BusinessException(LoanCodeEnum.SAVE_ERROR_UNKNOWN_ERROR);
        }
        if(null != loanDetail.getFlowId()){
            FlowEditDTO flowEditDTO = new FlowEditDTO();
            flowEditDTO.setFlowId(loanDetail.getFlowId());
            flowEditDTO.setTransactionType(LoanTypeEnum.getFlowType(loanDetail.getType()));
            flowEditDTO.setAmount(loanSummaryUpdateDTO.getAmount());
            String categoryCode = LoanTypeEnum.getCategoryCode(loanDetail.getType());
            Categories category = categoriesService.findByCateGoryCode(categoryCode, loanDetail.getUserId());
            if(null == category){
                log.warn("没有找到对应的分类型信息");
                throw new BusinessException(CategoryCodeEnum.CATEGORY_NOT_EXIST);
            }
            flowEditDTO.setCategoryId(category.getId());
            flowEditDTO.setAccountId(loanDetail.getAccountId());
            flowEditDTO.setTransactionDate(loanDetail.getLoanDate());
            flowEditDTO.setDescription(loanDetail.getRemarks());
            flowEditDTO.setUserId(loanDetail.getUserId());
            flowManageService.edit(flowEditDTO);
        }
        this.refreshData(loanDetail.getSummaryId());
        LoanDetail detail = this.loanDetailService.findById(loanSummaryUpdateDTO.getId(),loanSummaryUpdateDTO.getUserId());
        return DataConvertorUtils.copyProperties(detail,LoanSummaryVO::new);
    }

    @Override
    public LoanOverviewVO loanOverView(LoanSummaryQueryDTO loanSummaryQueryDTO) {
        LoanOverviewBO loanOverviewBO =  this.loanSummaryService.sumLoan(loanSummaryQueryDTO);
        LoanOverviewVO loanOverviewVO = new LoanOverviewVO();
        if(null == loanOverviewBO){
            return loanOverviewVO;
        }
        loanOverviewVO.setLoanTotal(AmountConverterUtils.convertFenToYuan(loanOverviewBO.getLoanTotal()));
        loanOverviewVO.setRepayTotal(AmountConverterUtils.convertFenToYuan(loanOverviewBO.getRepayTotal()));
        return loanOverviewVO;
    }

    @Override
    public LoanDetailOverviewVO loanDetailOverView(LoanDetailQueryDTO loanDetailQueryDTO) {
        LoanSummary loanSummary = this.loanSummaryService.findAndCheckById(loanDetailQueryDTO.getLoanSummaryId(),loanDetailQueryDTO.getUserId());
        //查询借款人信息
        BorrowerInfo borrowerInfo = this.borrowerInfoService.findAndCheckById(loanSummary.getBorrowerId(),loanDetailQueryDTO.getUserId());
        LoanDetailOverviewVO loanDetailOverviewVO = new LoanDetailOverviewVO();
        loanDetailOverviewVO.setBorrowerName(borrowerInfo.getName());
        loanDetailOverviewVO.setLoanTotal(AmountConverterUtils.convertFenToYuan(loanSummary.getTotalLoan()));
        loanDetailOverviewVO.setRepayTotal(AmountConverterUtils.convertFenToYuan(loanSummary.getTotalRepay()));
        loanDetailOverviewVO.setRemainTotal(AmountConverterUtils.convertFenToYuan(loanSummary.getRemainAmount()));
        BigDecimal loan = new BigDecimal(loanSummary.getTotalLoan());
        if(BigDecimal.ZERO.compareTo(loan) == 0){
            loanDetailOverviewVO.setRepayRate(100+"%");
        }else{
            BigDecimal repay = new BigDecimal(loanSummary.getTotalRepay());
            BigDecimal percent = repay.multiply(BigDecimal.valueOf(100))
                    .divide(loan, 2, RoundingMode.HALF_UP);
            loanDetailOverviewVO.setRepayRate(percent+"%");
        }
        return loanDetailOverviewVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    @RedisLock(key = " 'save_repay_record:' + #repayDetailSaveDTO.loanSummaryId")
    public LoanDetailVO saveRepay(RepayDetailSaveDTO repayDetailSaveDTO) {
        if(null == repayDetailSaveDTO.getAccountId() || 0 == repayDetailSaveDTO.getAccountId()){
            log.error("请选择还款账户");
            throw new BusinessException(LoanCodeEnum.PLEASE_CHOOSE_REPLY_ACCOUNT_ERROR);
        }
        LoanSummary loanSummary = this.loanSummaryService.findAndCheckById(repayDetailSaveDTO.getLoanSummaryId(),repayDetailSaveDTO.getUserId());
        if(GlobalConst.Loan.LOAN_STATUS_SETTLE.equalsIgnoreCase(loanSummary.getLoanStatus()) ){
            log.warn("当前账单已结清，无需重复还款");
            throw new BusinessException(LoanCodeEnum.BILL_SETTLE_NOT_REPAY_ERROR);
        }

        BorrowerInfo borrowerInfo = borrowerInfoService.findAndCheckById(loanSummary.getBorrowerId(), loanSummary.getUserId());
        if(null == borrowerInfo){
            log.error("选择的借款人不存在");
            throw new BusinessException(LoanCodeEnum.LOAN_BORROWER_NAME_NOT_EXISTS_ERROR);
        }
        Long flowId = null;
        if(repayDetailSaveDTO.isSaveFlow()){
            Categories categories = categoriesService.findByCateGoryCode("renqingshouru_huankuan",repayDetailSaveDTO.getUserId());
            if(categories == null ){
                log.error("当前用户没有借出的分类，可能被删除了！");
                throw new BusinessException(CategoryCodeEnum.CATEGORY_NOT_EXIST);
            }
            FlowSaveDTO flowSaveDTO  = new FlowSaveDTO();
            flowSaveDTO.setTransactionType(TransactionTypeEnum.INCOME.getCode());
            flowSaveDTO.setAmount(repayDetailSaveDTO.getRepayAmount());
            flowSaveDTO.setCategoryId(categories.getId());
            flowSaveDTO.setAccountId(repayDetailSaveDTO.getAccountId());
            flowSaveDTO.setTransactionDate(repayDetailSaveDTO.getRepayDate());
            flowSaveDTO.setUserId(repayDetailSaveDTO.getUserId());
            flowSaveDTO.setDescription(borrowerInfo.getName()+"还款");
            flowId = transactionsService.saveFlow(flowSaveDTO);
        }
        repayDetailSaveDTO.setBorrowerId(loanSummary.getBorrowerId());
        repayDetailSaveDTO.setFlowId(flowId);
        LoanDetail loanDetail = loanDetailService.saveRepay(repayDetailSaveDTO);
        if(null == loanDetail){
            throw new BusinessException(LoanCodeEnum.OPERATION_FAIL_ERROR);
        }
        boolean flag = loanSummaryService.saveRepay(repayDetailSaveDTO);
        if(!flag){
            throw new BusinessException(LoanCodeEnum.OPERATION_FAIL_ERROR);
        }
        return DataConvertorUtils.copyProperties(loanDetail,LoanDetailVO::new);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteLoanDetail(LoanDeleteDTO loanDeleteDTO) {
        LoanDetail loanDetail = this.loanDetailService.findById(loanDeleteDTO.getId(), loanDeleteDTO.getUserId());
        if(null == loanDetail){
            log.error("当前借款信息不存在");
            throw new BusinessException(LoanCodeEnum.DATA_NOT_EXISTS_ERROR);
        }
        if(null != loanDetail.getFlowId()){
            flowManageService.deleteFlow(loanDetail.getFlowId(),loanDetail.getUserId());
        }
        boolean flag = loanDetailService.removeLoanDetail(loanDeleteDTO.getId(), loanDeleteDTO.getUserId());
        if(!flag){
            log.error("删除失败，原因未知");
            throw new BusinessException(LoanCodeEnum.OPERATION_FAIL_ERROR);
        }
        this.refreshData(loanDetail.getSummaryId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteLoan(LoanDeleteDTO loanDeleteDTO) {
        LoanSummary loanSummary = this.loanSummaryService.findAndCheckById(loanDeleteDTO.getId(),loanDeleteDTO.getUserId());
        boolean flag = loanSummaryService.removeById(loanDeleteDTO.getId());
        if(!flag){
            log.error("删除失败，原因未知");
            throw new BusinessException(LoanCodeEnum.OPERATION_FAIL_ERROR);
        }
        List<Long> flowIds =  loanDetailService.findFlowIdsBySummaryId(loanSummary.getId(),loanDeleteDTO.getUserId());
        if(CollUtil.isNotEmpty(flowIds)){
            for (Long flowId : flowIds) {
                flowManageService.deleteFlow(flowId,loanDeleteDTO.getUserId());
            }
        }
        //删除当前借款下的所有记录信息
       flag = loanDetailService.removeAllRecordBySummaryId(loanSummary.getId(),loanDeleteDTO.getUserId());
        if(!flag){
            log.error("删除失败，原因未知");
            throw new BusinessException(LoanCodeEnum.OPERATION_FAIL_ERROR);
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveLoanHis(List<LoanSaveHisDTO> list) {
        if(CollUtil.isEmpty(list)){
            return ;
        }
        long userId = 513L;
        for (LoanSaveHisDTO loanSaveHisDTO : list) {
            BorrowerInfo borrowerInfo = borrowerInfoService.findByBorrowerName(loanSaveHisDTO.getName(), userId);
            Long borrowerId;
            if(null == borrowerInfo){
                LoanBorrowerSaveDTO loanBorrowerSaveDTO = new LoanBorrowerSaveDTO();
                loanBorrowerSaveDTO.setName(loanSaveHisDTO.getName());
                loanBorrowerSaveDTO.setPhone(null);
                loanBorrowerSaveDTO.setUserId(userId);
                BorrowerInfoVO borrowerInfoVO = borrowerInfoService.saveBorrower(loanBorrowerSaveDTO);
                borrowerId = borrowerInfoVO.getId();
            }else{
                borrowerId = borrowerInfo.getId();
            }
            LoanSummarySaveDTO loanSummarySaveDTO = new LoanSummarySaveDTO();
            loanSummarySaveDTO.setBorrowerId(borrowerId);
            loanSummarySaveDTO.setAmount(loanSaveHisDTO.getAmount());
            loanSummarySaveDTO.setAccountId(3L);
            loanSummarySaveDTO.setLoanType("loan");
            loanSummarySaveDTO.setLoanDate(loanSaveHisDTO.getData());
            loanSummarySaveDTO.setUserId(userId);
            this.saveLoan(loanSummarySaveDTO);
        }
    }

    private void refreshData(Long summaryId) {
        Integer loanTotal = this.loanDetailService.sumLoanDetail(summaryId, LoanTypeEnum.LOAN_TYPE_LOAN.getCode());
        Integer repayTotal = this.loanDetailService.sumLoanDetail(summaryId, LoanTypeEnum.LOAN_TYPE_REPAY.getCode());
        this.loanSummaryService.refreshData(summaryId,loanTotal,repayTotal);
    }
}
